#!/usr/bin/env bash

include "objectv5.sh"
include "test.sh"

ObjectV5::DefineClass Git "
    --cargs=path
    --string=path
"

function Git::IsValid()
{
    [ $# == 0 ] && assert_fail "this"
    local this="$1"; shift
    local path="$(Git::path "$this")"
    pushd "$path" 1>/dev/null || return
    git branch 1>/dev/null 2>&1
    local res="$?"
    popd 1>/dev/null
    return "$res"
}

function Git::GetHash()
{
    [ $# == 0 ] && assert_fail "this"
    local this="$1"; shift
    local path="$(Git::path "$this")"
    pushd "$path" 1>/dev/null || return
    git rev-parse "$1"
    local res="$?"
    popd 1>/dev/null
    return "$res"
}

function Git::GetRemoteHash()
{
    Git::GetHash "$1" origin/master
}

function Git::GetBranchName()
{
    [ $# == 0 ] && assert_fail "this"
    local this="$1"; shift
    local path="$(Git::path "$this")"
    pushd "$path" 1>/dev/null || return
    git name-rev --no-undefined --name-only --exclude="remotes/*" "$1"
    local res="$?"
    popd 1>/dev/null
    return "$res"
}

function Git::Checkout()
{
    [ $# == 0 ] && assert_fail "this"
    local this="$1"; shift
    local path="$(Git::path "$this")"
    pushd "$path" 1>/dev/null || return
    git checkout "$1"
    local res="$?"
    popd 1>/dev/null
    return "$res"
}

function Git::MoveTo()
{
    [ $# == 0 ] && assert_fail "this"
    local this="$1"; shift
    local path="$(Git::path "$this")"
    pushd "$path" 1>/dev/null || return
    git reset --hard "$1"
    local res="$?"
    popd 1>/dev/null
    return "$res"
}

function Git::Fetch()
{
    [ $# == 0 ] && assert_fail "this"
    local this="$1"; shift
    local path="$(Git::path "$this")"
    pushd "$path" 1>/dev/null || return
    git fetch "${1-origin}"
    local res="$?"
    popd 1>/dev/null
    return "$res"
}

function Git::IsModified()
{
    [ $# == 0 ] && assert_fail "this"
    local this="$1"; shift
    local path="$(Git::path "$this")"
    pushd "$path" 1>/dev/null || return
    (( $(git status -s | grep -v "^??" | wc -l) != 0 ))
    local res="$?"
    popd 1>/dev/null
    return "$res"
}

function Git::SubmoduleUpdate()
{
    [ $# == 0 ] && assert_fail "this"
    local this="$1"; shift
    local path="$(Git::path "$this")"
    pushd "$path" 1>/dev/null || return
    git submodule init && git submodule update
    local res="$?"
    popd 1>/dev/null
    return "$res"
}

function Git::Test1()
{
    local testgit1="$(test::get_temp_path git.test1)"
    local testgit2="$(test::get_temp_path git.test1)"
    local testgit3="$(test::get_temp_path git.test1)"
    Git git1{"$testgit2"}
    Git git2{"$testgit3"}

    function Git::Test1::BeforeTest()
    {
        # 1. init
        mkdir "$testgit1"
        cd "$testgit1"
        git init --bare

        # 2. push 2commits at 1
        git clone "file://${testgit1}" "$testgit2"
        cd "$testgit2"
        echo 1 > 1
        git add .
        git commit -m "1"
        git branch 1
        echo 2 > 2
        git add .
        git commit -m "2"
        git push origin master

        # 3. create branch dev
        git branch dev

        # 4. clone at 2
        git clone "file://${testgit1}" "$testgit3"

        # 5. push 1more commit at 1
        cd "$testgit2"
        echo 3 > 1
        git add .
        git commit -m "3"
        git push origin master

        # 6. goto home
        cd
        return
    }

    function Git::Test1::AfterTest()
    {
        rm -rf "$testgit1"
        rm -rf "$testgit2"
        rm -rf "$testgit3"
        return
    }

    # IsValid
    test::should_return_false Git::IsValid git1
    test::should_return_false Git::IsValid git2
    slient                    Git::Test1::BeforeTest
    test::should_return_true  Git::IsValid git1
    test::should_return_true  Git::IsValid git2

    # GetHash, GetRemoteHash
    local hash1="$(Git::GetHash git1 1)"
    local hash2="$(Git::GetHash git1 dev)"
    local hash3="$(Git::GetHash git1 master)"

    # Checkout, GetBranchName
    test::should_return_true  Git::Checkout      git1 master
        test::run_cmd         Git::GetBranchName git1 HEAD
        test::should_be       "master"
    test::should_return_true  Git::Checkout      git1 dev
        test::run_cmd         Git::GetBranchName git1 HEAD
        test::should_be       "dev"
    test::should_return_false Git::Checkout      git1 unknown
        test::run_cmd         Git::GetBranchName git1 HEAD
        test::should_be       "dev"
    slient                    Git::Checkout      git1 master
        test::run_cmd         Git::GetBranchName git1 HEAD
        test::should_be       "master"

    # MoveTo
    slient                    Git::Checkout git1 dev
    test::should_return_true  Git::MoveTo   git1 master~2
        test::run_cmd         Git::GetHash  git1 dev
        test::should_be       "$hash1"

    # Fetch
        test::run_cmd         Git::GetRemoteHash git2
        test::should_be       "$hash2"
    test::should_return_true  Git::Fetch         git2
        test::run_cmd         Git::GetRemoteHash git2
        test::should_be       "$hash3"

    slient                    Git::Test1::AfterTest
    return
}
test::register_test Git::Test1

function Git::Test2()
{
    local testgit="$(test::get_temp_path git.test2)"
    Git git{"$testgit"}

    function Git::Test2::BeforeTest()
    {
        # 1. init
        mkdir "$testgit"
        cd "$testgit"
        git init

        echo 1 > 1
        git add .
        git commit -m "1"
        return
    }

    function Git::Test2::AfterTest()
    {
        rm -rf "$testgit"
        return
    }

    # IsModified
    slient                    Git::Test2::BeforeTest
    test::should_return_false Git::IsModified git
    touch 2
    test::should_return_false Git::IsModified git
    echo 2 > 1
    test::should_return_true  Git::IsModified git
    slient                    Git::Test2::AfterTest
    return
}
test::register_test Git::Test2
